package Jogo.States {
	import Jogo.Comm.FlashSocket;
	import Jogo.Comm.Message;
	import Jogo.Comm.MsgHandler;
	import Jogo.Entities.Enemy;
	import Jogo.Entities.Entity;
	import Jogo.Entities.Tile;
	import Jogo.Entities.Turret;
	import Jogo.Entities.TurretPoint;
	import Jogo.Main;
	
	import flash.desktop.NativeApplication;
	import flash.display.Bitmap;
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.events.TimerEvent;
	import flash.geom.Point;
	import flash.system.System;
	import flash.text.TextField;
	import flash.text.TextFieldType;
	import flash.text.TextFormat;
	import flash.ui.Keyboard;
	import flash.utils.getDefinitionByName;
	
	import mx.controls.Alert;
	import mx.events.CloseEvent;
	import mx.validators.EmailValidator;
	
	import spark.components.TextArea;
	import spark.components.TextInput;
	
	/** Class to define the first phase
	 * of the game
	 * 
	 * @author Bruno Zumba (28/07/2011)
	 */
	public class PhaseState extends State{		
		/* A return from the 'enterFrame' function of the enemy */
		public static const ENEMY_WALKED			: int = 0;
		public static const ENEMY_WON				: int = 1;
		public static const ENEMY_DIED				: int = 2;
		
		/* Side panel */
		[Embed(source = "../../Img/PhaseState/sidePanel_certo.png")]
		private var sidePanelBMP:Class;
		private var sidePanel : Bitmap;
		private const SIDE_PANEL_WIDTH				: int = 256;
		private const SIDE_PANEL_HEIGHT				: int = 768;
		
		[Embed(source = "../../Img/PhaseState/ganhou_jogo.png")]
		private var gameStateWonBMP:Class;
		[Embed(source = "../../Img/PhaseState/perdeu_jogo.png")]
		private var gameStateLoseBMP:Class;
		
		
		/* map tiles */
		protected static var tiles : Vector.<Vector.<Tile>>;
		
		public static var tileSide : int;
		
		/* The IP of the players in the game. IP from player 0 = players[0] */
		private static var players : Vector.<String>;
		private static var money	: int;
		protected var hp : int;
		
		private var gameState : int;
		private var gameFinishedPanel: Bitmap;
		private const GAME_RUNNING : int = 0;
		private const GAME_WON : int = 1;
		private const GAME_LOST : int = 2;
		
		/* The number of waves this phase will have */
		protected var TOTAL_WAVES : int; 
		
		/* Vector to describe each wave of this phase */
		protected var wave : Vector.<Enemy>;
		protected var currentWave : int;
		
		protected var enemies : Vector.<Enemy>; /** PROTECTED POR ENQTO */
		private static var turrets : Vector.<Turret>;
		protected static var turretPoints : Vector.<TurretPoint>;
		
		private var mainInstance : Main;
		
		/* Variables to get rid of timers when dispatching enemies */
		protected var dispatchingEnemy : Boolean;
		protected var dispatchEnemyCounter : int;
		
		private var flashSckt : FlashSocket;
		
		/* TextFields for the side panel */
		private var format : TextFormat;
		protected var moneyTF : TextField;
		private const moneyTFPoint : Point = new Point(130, 460);
		protected var playersTF : TextField;
		private const playersTFPoint : Point = new Point(150, 40);
		protected var wavesTF : TextField;
		private const wavesTFPoint : Point = new Point(130, 660);
		private var currentPhaseTF : TextField;
		private const currentPhaseTFPoint : Point = new Point(10, 200);
		private var hpTF : TextField;
		private const hpTFPoint : Point = new Point(140, 240);
		protected var sidePanelRoot : MovieClip;
		
		/* Test stuffs */
		public static var currentPlayer : TextField = new TextField(); 
		public function PhaseState() {
			mainInstance = Main.getInstance();
			
			root = new MovieClip();
			enemies = new Vector.<Enemy>;
			turrets = new Vector.<Turret>;
			turretPoints = new Vector.<TurretPoint>;
			wave = new Vector.<Enemy>;
			players = new Vector.<String>;
			players.push((-1).toString());
			
			setSidePanel();
			
			gameState = GAME_RUNNING;
			
			flashSckt = FlashSocket.getInstance();
			
			currentWave = 0;
			
			money = 1000;			

			dispatchEnemyCounter = 0;
			
			dispatchingEnemy = false;
			
			root.addEventListener(MouseEvent.CLICK, mainInstance.click);
			
		}
		
		private function setSidePanel():void{
			sidePanelRoot = new MovieClip();
			sidePanel = new sidePanelBMP();
			sidePanel.width = SIDE_PANEL_WIDTH;
			sidePanel.height = SIDE_PANEL_HEIGHT;
			sidePanel.x = 0;
			sidePanel.y = 0;
			format = new TextFormat();
			format.font = "Discognate Regular";
			format.color = 0x000000;
			
			//Set the money text field
			moneyTF = new TextField();
			moneyTF.x = moneyTFPoint.x;
			moneyTF.y = moneyTFPoint.y;
			format.size = 45;
			moneyTF.defaultTextFormat = format;
			
			//Set the players text field
			playersTF = new TextField();
			playersTF.x = playersTFPoint.x;
			playersTF.y = playersTFPoint.y;
			format.size = 70;
			playersTF.defaultTextFormat = format;
			
			//set the waves textfield
			wavesTF = new TextField();
			wavesTF.x = wavesTFPoint.x;
			wavesTF.y = wavesTFPoint.y;
			format.size = 40;
			wavesTF.defaultTextFormat = format;
			
			//Set the hp textfield
			hpTF = new TextField();
			hpTF.x = hpTFPoint.x;
			hpTF.y = hpTFPoint.y;
			format.size = 70;
			hpTF.defaultTextFormat = format;
			
			sidePanelRoot.addChild(sidePanel);
			sidePanelRoot.addChild(moneyTF);
			sidePanelRoot.addChild(playersTF);
			sidePanelRoot.addChild(wavesTF);
			sidePanelRoot.addChild(hpTF);
			
			root.addChild(sidePanelRoot);
		}
		
		public static function createRedefMessage():void{
			
			//Create the arrays Entity x Players for ENTITY_REDEFINITION message
			var pla : Vector.<int> = new Vector.<int>; //The player
			var pts : Vector.<Point> = new Vector.<Point>; //The point of the entity
			var entityType : Vector.<int> = new Vector.<int>; //The type of the entity (Turret1, Turret2 or Turret Point)
			var entityLevel : Vector.<int> = new Vector.<int>; //The level of the entity, if it's a turret1 or turret2
			
			var i : int;
			for (i = 0; i<turretPoints.length; i++){
				pts.push(turretPoints[i].getPoint());
				pla.push(turretPoints[i].getPlayer());
				entityType.push(Turret.TURRET_POINT);
				entityLevel.push(-1);
			}
			
			for (i = 0; i<turrets.length; i++){
				pts.push(turrets[i].getPoint());
				pla.push(turrets[i].getPlayer());
				entityType.push(turrets[i].getType());
				entityLevel.push(turrets[i].getLevel());
			}
			
			MsgHandler.createEntityRedefinitionMessage(pla, pts, entityType, entityLevel, players);
		}
		
		public static function upgradeTurret(x:int, y:int, sender:String):void{
			var turret:Turret = tiles[y][x].getTurret();
			
			//Check if the player who sent the message is the owner of the turret, if the turret is not null 
			//and if it's not in the max level
			if((players.indexOf(sender) != turret.getPlayer()) ||
				(turret == null) || (turret.getLevel() >= 3)){
				return;
			}
			
			
			if(money < turret.getCost(turret.getLevel()+1)){
				MsgHandler.createErrorMessage(players[turret.getPlayer()], "Not enought money to create the turret.");
				return;
			}
			money -= turret.getCost(turret.getLevel()+1);
			
			if (turret.levelUp()) { //leveld up
				MsgHandler.createEntityUpdateMessage(MsgHandler.UPGRADE, x, y, 0, players);
			}
		}
		
		public static function addMoney(money_p:int):void{
			money += money_p;
		}
		
		public static function sellTurret(x:int, y:int, sender:String):void{
			var turret : Turret = tiles[y][x].getTurret();
			
			//Check if the player who sendt the message is the owner of the turret
			if(players.indexOf(sender) != turret.getPlayer()){
				return;
			}
			
			if(turret == null) {
				return;
			}
			
			for(var lvl : int = turret.getLevel(); lvl > 0; lvl--){
				//The player gets 70% of what he spent building/upgrading the turret
				money = money + (turret.getCost(lvl)*0.7); 
			}
			
			
			//Remove the turret
			root.removeChild(turret.getGraphicsRoot());
			turrets.splice(turrets.indexOf(turret), 1);
			var player : int = turret.getPlayer();
			var price : Number = turret.sell();
			
			//Add the turret point
			var tp : TurretPoint = new TurretPoint(new Point(x,y));
			turretPoints.push(tp);
			tp.setPlayer(player);
			root.addChild(tp.getGraphicsRoot());
			
			MsgHandler.createEntityUpdateMessage(MsgHandler.SELL, x, y, 0, players);
		}
		
		public static function addPlayer(ip:String):void{
			players.push(new String(ip));
			
			
			sortTurretPoints();
			sortTurrets();
			createRedefMessage();
		}
		public static function removePlayer(ip:String):void{
			
			var pos : int =	players.lastIndexOf(ip);
			if (pos == -1){
				return;
			}

			players.splice(pos, 1);
			
			
			sortTurretPoints();
			sortTurrets();
			createRedefMessage();
		}
		
		public static function createTurret(turretType:int, pt:Point, sender:String):void{
			var turret : Turret = new Turret(turretType, pt);
			
			
			var tp : TurretPoint = tiles[pt.y][pt.x].getTurretPoint();
			
			if (tp == null){
				return;
			}
			
			//Check if the player who sendt the message is the owner of the turret
			if (players.indexOf(sender) != tp.getPlayer()){ 
				return;
			}
			
			if (money < turret.getCost(1)){ //Not enough money
				MsgHandler.createErrorMessage(players[tp.getPlayer()], "Not enought money to create the turret.");
				return; 
			}
			
			money -= turret.getCost(1);
		
			
			//Remove the turret point
			root.removeChild(tp.getGraphicsRoot());
			turretPoints.splice(turretPoints.indexOf(tp), 1);
			var player : int = tp.getPlayer();
			tp.destroy();
			
			//Add the turret
			turret.setPlayer(player);
			turrets.push(turret);
			root.addChild(turret.getGraphicsRoot());
			
			MsgHandler.createEntityUpdateMessage(MsgHandler.CREATE, pt.x, pt.y, turretType, players);
		}
		
		
		/* When a player connects/disconnects the game, it needs
		 * to sort the turrets and the turrets points among the
		 * among the current players
		 */
		protected static function sortTurretPoints() : void{
			var i : int;
			if (players.length == 1){ //All turret points belongs to "player 0"
				for (i=0; i < turretPoints.length; i++){
					turretPoints[i].setPlayer(0);
				}
			} else {
			
				var p : int = Math.floor(1+Math.random()*(players.length-1));
				for (i=0; i < turretPoints.length; i++){
					turretPoints[i].setPlayer(p++);
					p = (p > (players.length-1) ? 1 : p);
				}
			}
		}
		private static function sortTurrets() : void {
			/* Sort the turrets based on it's levels */
			var array : Vector.<Vector.<Turret>> = new Vector.<Vector.<Turret>>(Turret.MAX_LEVEL+1);
			var i : int;
			
			if(players.length == 1){
				for (i=0; i < turrets.length; i++){
					turrets[i].setPlayer(0);
				}
				return;
			}
			
			for (i=0; i<=Turret.MAX_LEVEL;i++){
				array[i] = new Vector.<Turret>;
			}
			
			/* The array[0] contains all turrets in level 0.
			 * array[1] contains all turrets in level 1 and so on */
			for (i = 0; i<turrets.length; i++){
				array[turrets[i].getLevel()].push(turrets[i]);
			}
			
			/* Redistribute the turrets among the players */
			var p : int = Math.floor(1+Math.random()*(players.length-1));
			for(i = 1; i < array.length; i++){
				while(array[i].length > 0){
					array[i].shift().setPlayer(p++);
					p = (p > (players.length-1) ? 1 : p)
				}
			}
		}
		
		
		protected function callNextWave(newWave:Vector.<Enemy>) : void{ 
			this.wave = newWave;
			dispatchingEnemy = true;
			dispatchEnemyCounter = 0;
		}
		
		private function dispatchEnemy() : void { 
			dispatchEnemyCounter++;
			
			if(wave.length > 0){
				if (dispatchEnemyCounter > 1*Main.FRAME_RATE){
					dispatchEnemyCounter = 0;
					var enemyTemp : Enemy = wave.shift();
					enemies.push(enemyTemp);
					root.addChild(enemyTemp.getGraphicsRoot());
					enemyTemp.setInGame(true);
				}
			} else {
				dispatchingEnemy = false;
			}
		}
		
		override public function assume(previousState : State) : void {
			if (previousState != null){
				mainInstance.removeGraphics(previousState.getGraphicsRoot());
			}
			
			mainInstance.addGraphics(root);
		}
		
		override public function reassume(previousState : State) : void {
		}
		
		override public function leave() : void {
		}
		
		public static function destroy() : void {
			Alert.show("This application has lost connection with the Framework.", "Connection Error", Alert.OK, null, okHandler);
			//Remove all the players
			for(var i:int = 1; i< players.length; i++){
				players.pop();
			}
			sortTurretPoints();
			sortTurrets();
			createRedefMessage();
			
		}
		// Event handler function for displaying the selected Alert button.
		private static function okHandler(evt:CloseEvent):void {
			if (evt.detail == Alert.OK) {
				var nativeApp:Object =  getDefinitionByName("flash.desktop.NativeApplication");
				nativeApp.nativeApplication.exit();

			}
		}
		
		
		override public function enterFrame(e : Event) : void {
			//Update the text fields in the side panel
			playersTF.text = ((players.length-1).toString());
			moneyTF.text = (money.toString());
			if(currentWave > TOTAL_WAVES){
				wavesTF.text = (TOTAL_WAVES+"/"+TOTAL_WAVES);
			} else {
				wavesTF.text = (currentWave+"/"+TOTAL_WAVES);
			}
			hpTF.text = (hp.toString());
			//currentPhaseTF.text = ("Fase atual: ");
			
			if((gameState == GAME_LOST) || (gameState == GAME_WON)){
				root.addChild(gameFinishedPanel);
				return;
			} 
			
			
			
			if (players.length <= 1){ //If there's no player, the game won't move
				return;
			}
			
			/* Check if it is dispatching enemies */
			if ((dispatchingEnemy) && (players.length > 1)){
				dispatchEnemy();
			}
			
			var i : int;
			
			var enemyStatus : int;
			//Update the enemies
			for(i=0; i< enemies.length; i++){
				enemyStatus = enemies[i].enterFrame();
				switch(enemyStatus){
					case ENEMY_WON:
						root.removeChild(enemies[i].getGraphicsRoot());
						hp--;
						enemies.splice(i, 1);
					break;
					case ENEMY_DIED:
						root.removeChild(enemies[i].getGraphicsRoot());
						enemies.splice(i, 1);
					break;
				}
			}
			
			
			
			//Check if the game is over either by winning of loosing the game
			if(hp <=0) {
				MsgHandler.createGameStateMessage(MsgHandler.LOSE, players);
				gameState = GAME_LOST;
				gameFinishedPanel = new gameStateLoseBMP();
				
				gameFinishedPanel.x = 0;
				gameFinishedPanel.y = 0;
				gameFinishedPanel.width = Main.GAME_WIDTH;
				gameFinishedPanel.height = Main.GAME_HEIGHT;
				
				root.addEventListener(MouseEvent.CLICK, mainInstance.exit);
				
			} else if((enemies.length == 0) && (currentWave > TOTAL_WAVES)){//todos os inimigos se foram
				MsgHandler.createGameStateMessage(MsgHandler.WIN, players);
				gameState = GAME_WON;
				gameFinishedPanel = new gameStateWonBMP();
				gameFinishedPanel.x = 0;
				gameFinishedPanel.y = 0;
				gameFinishedPanel.width = Main.GAME_WIDTH;
				gameFinishedPanel.height = Main.GAME_HEIGHT;
				
				root.addEventListener(MouseEvent.CLICK, mainInstance.exit);
			}

			
			//Update the turrets
			for(i=0; i<turrets.length; i++){
				turrets[i].enterFrame();
			}
		}
		
		public static function getTilesMatrix() : Vector.<Vector.<Tile>>{
			return tiles;
		}	
		
		public static function debugar():void{
			trace("Turrets: ");
			var i:int;
			for (i=0; i<turrets.length; i++){
				trace(turrets[i].getPoint().x +", "+turrets[i].getPoint().y + ". Dono: " + turrets[i].getPlayer());
			}
			trace("\n\nTurret Points: ");
			for (i=0; i<turretPoints.length; i++){
				trace(turretPoints[i].getPoint().x +", "+turretPoints[i].getPoint().y + ". Dono: " + turretPoints[i].getPlayer());
			}
		}
	}
}